#ifndef VECTORX_HPP
#define VECTORX_HPP

// Ver. 3

#include "VectorX.h"
#include <cmath>
#include <iostream>

namespace sMathLib
{

template<typename T, int dimension>
VectorX<T, dimension>::VectorX()
{
	for(int index = 0; index != dimension; ++index)
	{
		raw_data[index] = T();
	}
}

template<typename T, int dimension>
VectorX<T, dimension>::VectorX(T x, T y, T z)
{
	raw_data[0] = x;
	raw_data[1] = y;
	raw_data[2] = z;

	if(dimension > 3)
	{
		for(unsigned index = 4; index != dimension; ++index)
			raw_data[index] = T();
	}
}

template<typename T, int dimension>
VectorX<T, dimension>::VectorX(const VectorX &rhs)
{
    *this = rhs;
}

template<typename T, int dimension>
VectorX<T, dimension> VectorX<T, dimension>::clone() const
{
	return VectorX(*this);
}

template<typename T, int dimension>
T& VectorX<T, dimension>::operator[](const int index)
{
	return raw_data[index];
}

template<typename T, int dimension>
const T& VectorX<T, dimension>::operator[](const int index) const
{
	return this->raw_data[index];
}

template<typename T, int dimension>
VectorX<T, dimension>& VectorX<T, dimension>::operator=(const VectorX& rhs)
{
	for(int index = 0; index != dimension; ++index)
	{
		raw_data[index] = rhs.raw_data[index];
	}

    return *this;
}

template<typename T, int dimension>
VectorX<T, dimension>& VectorX<T, dimension>::operator+=(const VectorX& rhs)
{
	for(int index = 0; index != dimension; ++index)
	{
		raw_data[index] += rhs.raw_data[index];
	}

	return *this;
}

template<typename T, int dimension>
VectorX<T, dimension>& VectorX<T, dimension>::operator-=(const VectorX& rhs)
{
	for(int index = 0; index != dimension; ++index)
	{
		raw_data[index] -= rhs.raw_data[index];
	}

	return *this;
}

template<typename T, int dimension>
VectorX<T, dimension>& VectorX<T, dimension>::operator*=(const T& rhs)
{
	for(int index = 0; index != dimension; ++index)
	{
		raw_data[index] *= rhs;
	}

	return *this;
}

template<typename T, int dimension>
const T* VectorX<T, dimension>::data() const
{
	return raw_data;
}


template<typename T, int dimension>
T VectorX<T, dimension>::dot(const VectorX& rhs) const
{
	T ret();

	for(unsigned index = 0; index != dimension; ++index)
		ret += raw_data[index]*rhs[index];

	return ret;	
}

/// Abs, only for double, float, int
template<typename T, int dimension>
T VectorX<T, dimension>::abs() const
{
	T ret();

	for(unsigned index = 0; index != dimension; ++index)
		ret += raw_data[index] * raw_data[index];

	return std::sqrt(ret);
}

/// NormSqr
template<typename T, int dimension>
T VectorX<T, dimension>::normSqr() const
{	
	T ret;
	for(int index = 0; index != dimension; ++index)
		ret += raw_data[index] * raw_data[index];

	return ret;
}

/// Volume
template<typename T, int dimension>
T VectorX<T, dimension>::volume() const
{
	T ret;
	for(unsigned index = 0; index != dimension; ++index)
	{
		ret *= raw_data[index];
	}

	return ret;
}

/// Shift
template<typename T, int dimension>
T VectorX<T, dimension>::shift(int index, T& offset) const
{
	try
	{
		if(index > dimension-1)
			throw "index is larger than total elements number.";
	}
	catch(const char* err)
	{
		std::cout << err << std::endl;

		return 1;
	}

	return raw_data[index] + offset;
}

template<typename T, int dimension>
VectorX<T, dimension>  VectorX<T, dimension>::shift(T offsetX, T offsetY, T offsetZ) const
{
	VectorX<T, dimension> ret;
	ret[0] = offsetX+this->x();
	ret[1] = offsetY+this->y();
	ret[2] = offsetZ+this->z();
	return ret;
}

template<typename T, int dimension>
T VectorX<T, dimension>::shiftFrom(T& point, T& offset)
{
	return point + offset;
}

/// Arithmetic operators
template<typename T, int dimension>
sMathLib::VectorX<T, dimension> operator+(const sMathLib::VectorX<T, dimension>& lhs, const sMathLib::VectorX<T, dimension>& rhs)
{
	//sMathLib::VectorX ret(lhs);

	VectorX<T, dimension> ret(lhs);

	ret += rhs;

	return ret;
}

template<typename T, int dimension>
sMathLib::VectorX<T, dimension> operator-(const sMathLib::VectorX<T, dimension>& lhs, const sMathLib::VectorX<T, dimension>& rhs)
{
	sMathLib::VectorX ret(lhs);

	ret -= rhs;

	return ret;
}

template<typename T, int dimension>
sMathLib::VectorX<T, dimension> operator*(const sMathLib::VectorX<T, dimension>& lhs, const T& rhs)
{
	sMathLib::VectorX ret(lhs);

	ret *= rhs;

	return ret;
}

template<typename T, int dimension>
std::ostream& operator<<(std::ostream& ostr, const VectorX<T, dimension>& rhs)
{
	ostr << "(";
	for(unsigned index = 0; index != dimension; ++index)
	{
		if(index != dimension-1)
			ostr << rhs[index] << ", ";
		else
			ostr << rhs[index] << ")";
	}
	return ostr;
}

} // sMathLib



#endif // VECTORX_HPP

